package com.sk.bilibili.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sk.bilibili.bean.*;
import com.sk.bilibili.dto.FocusInfo;
import com.sk.bilibili.dto.MemberInfo;
import com.sk.bilibili.event.BiliBiliEvent;
import com.sk.bilibili.other.R;
import com.sk.bilibili.utils.*;
import org.jasypt.util.password.PasswordEncryptor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import com.sk.bilibili.mapper.MemberMapper;
import org.springframework.util.StringUtils;

/**
* @author 邵康
* @date 2021/11/22 23:08
*/
@Service
public class MemberService extends ServiceImpl<MemberMapper,Member> {

    @Autowired
    private MinioService minioService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private PasswordEncryptor encryptor;
    @Autowired
    private DbUtils dbUtils;
    @Autowired
    private FocusService focusService;
    @Autowired
    private DynamicService dynamicService;
    @Autowired
    private MemberExtendService memberExtendService;
    @Autowired
    private VideoSetService videoSetService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    private static final String AVATAR_PATH="member/avatar/%s.jpg";
    private static final Pattern AVATAR_PATTERN=Pattern.compile("/(member/avatar/.*jpg)");

    public R<Void> add(Member member) {
        member.setAvatar(minioService.add(member.getAvatarFile(),AVATAR_PATH));
        save(member);
        return R.success();
    }

    public R<Void> delete(Integer id) {
        Member temp = getById(id);
        minioService.delete(temp.getAvatar(),AVATAR_PATTERN);
        removeById(id);
        return R.success();
    }

    public R<Void> update(Member member) {
        if(containsName(member))return R.error("用户名已存在");
        Member old = getById(BSessionUtils.getMemberId());
        if(member.getNickName()!=null&&!old.getNickName().equals(member.getNickName())){
            if(old.getCoinNum()<5){
                return R.error("用户硬币不足");
            }else {
                changeCoin(old.getId(),-5,"修改昵称");
                member.setCoinNum(null);//防止覆盖 硬币数
            }
        }
        String avatar = minioService.update(AVATAR_PATH, AVATAR_PATTERN, old.getAvatar(), member.getAvatarFile());
        member.setAvatar(avatar);
        updateById(member);
        return R.success();
    }

    private boolean containsName(Member member) {
        QueryWrapper<Member> queryWrapper = QueryWrapperUtils.eq("nick_name", member.getNickName());
        queryWrapper.ne("id",member.getId());
        return count(queryWrapper)>0;
    }

    public R<IPage<Member>> page(Integer page, Integer size) {
        return R.success(page(new Page<>(page,size),null));
    }

    public R<Member> register(MemberInfo memberInfo) {
        Member member = memberInfo.getMember();
        if(containsName(member))return R.error("用户名已存在");
        String phone = member.getPhone();
        QueryWrapper<Member> queryWrapper= QueryWrapperUtils.eq("phone",phone);
        if(count(queryWrapper)>0)return R.error("此电话已被注册");
        String rawCode = redisUtils.get(phone);
        if(rawCode==null)return R.error("验证码已失效");
        if(!rawCode.equals(memberInfo.getCode()))return R.error("验证码错误");
        String password = member.getPassword();
        member.setPassword(encryptor.encryptPassword(password));
        save(member);
        member.setPassword(password);//保留注册后的原密码  用于立即登录
        eventPublisher.publishEvent(new BiliBiliEvent<>(this,member));
        return R.success(member);
    }

    public R<Member> login(MemberInfo memberInfo) {
        Member member = memberInfo.getMember();
        QueryWrapper<Member> queryWrapper= QueryWrapperUtils.eq("phone",member.getPhone());
        Member raw = getOne(queryWrapper);
        if(raw==null)return R.error("用户不存在");
        if(StringUtils.isEmpty(member.getPassword())){
            String code = redisUtils.get(member.getPhone());
            if(code==null)return R.error("验证码过期");
            if(!code.equals(memberInfo.getCode()))return R.error("验证码错误");
            handleMember(raw);
            return R.success(raw);
        }else {
            if(encryptor.checkPassword(member.getPassword(),raw.getPassword())){
                handleMember(raw);
                return R.success(raw);
            }else {
                return R.error("密码错误");
            }
        }
    }

    private void handleMember(Member member) {
        String bSessionId = BSessionUtils.setMemberId(member.getId());
        member.getExtra().put("bSessionId",bSessionId);
    }

    public IPage<Member> focusMember(Page<Member> page, FocusInfo focusInfo) {
        return baseMapper.focusMember(page,focusInfo);
    }

    public void changeNum(String column, Integer num, Integer id) {
        dbUtils.changeNum("member",column,num,id);
    }

    public List<Member> followMember(int memberId) {
        return baseMapper.followMember(memberId);
    }

    public R<Member> getMember(Integer id) {
        int memberId = BSessionUtils.getMemberId();
        if(id==null)id= memberId;
        Member member = getById(id);
        member.getExtra().put("focusNum",focusService.getFocusNum(member.getId()));
        member.getExtra().put("dynamicNum",dynamicService.getDynamicNum(member.getId()));
        member.getExtra().addExtra(memberExtendService.getById(id));
        member.getExtra().put("playNum",videoSetService.getPlayNum(id));
        warpFocus(memberId, member);
        return R.success(member);
    }

    public void warpFocus(int memberId, Member member) {
        member.getExtra().put("focus",focusService.getFocus(memberId,member.getId()));
    }

    public String getNickName(Integer memberId) {
        return baseMapper.getNickName(memberId);
    }

    public IPage<Member> searchMember(Page<Member> page, QueryWrapper<Member> queryWrapper) {
        IPage<Member> iPage = page(page, queryWrapper);
        iPage.getRecords().forEach(this::warpVideoSetNum);
        int memberId = BSessionUtils.getMemberId();
        iPage.getRecords().forEach(member -> warpFocus(memberId,member));
        return iPage;
    }

    private void warpVideoSetNum(Member member) {
        member.getExtra().put("videoSetNum",videoSetService.getVideoSetNum(member.getId()));
    }

    @EventListener(condition = "event.type == 'com.sk.bilibili.service.CoinService.add'")
    public void handleCoinAdd(BiliBiliEvent<Coin> event){
        Coin coin = event.getData();//扣减原来人的硬币
        Integer memberId = event.getExtra("memberId");
        changeCoin(memberId,-coin.getNum(),"视频投币");
        if(coin.getType()!=0)return;
        VideoSet videoSet = videoSetService.getById(coin.getTargetId());
        changeCoin(videoSet.getMemberId(),coin.getNum(),"投稿视频收益");
    }

    // 修改硬币 数量 单独  API  有特殊用处  调用 该方法意味  修改 一定合法
    public void changeCoin(Integer memberId,Integer changeNum,String reason){
        changeNum("coin_num", changeNum, memberId);
        CoinRecord coinRecord = new CoinRecord();
        coinRecord.setChangeValue(changeNum);
        coinRecord.setMemberId(memberId);
        coinRecord.setReason(reason);
        eventPublisher.publishEvent(new BiliBiliEvent<>(this,coinRecord));
    }

    public R<Void> logout() {
        BSessionUtils.invalidate();
        return R.success();
    }

    public R<Void> dailyLogin() {
        Long day = TimeUtils.getDay();
        Long lastLoginDay= BSessionUtils.getLastLoginDay();
        if(!day.equals(lastLoginDay)){
            eventPublisher.publishEvent(new BiliBiliEvent<>(this, BSessionUtils.getMemberId()));
        }
        BSessionUtils.setLastLoginDay(day);
        return R.success();
    }

    public Boolean openHistory(Integer memberId) {
        return baseMapper.openHistory(memberId);
    }

    @EventListener(condition = "event.type == 'com.sk.bilibili.service.FocusService.switchFocus'")
    public void handleFocusSwitch(BiliBiliEvent<Focus> event) {
        Focus focus = event.getData();
        Integer focusId=event.getExtra("focusId");
        redisUtils.changeNum("Member-"+focusId,"fansNum",focus == null ? -1 : 1);
    }

}
